package com.tn.openrpc.stomp;

import com.kaazing.gateway.client.protocol.stomp.StompClient;

import com.tn.openrpc.TimeoutException;
import com.tn.openrpc.channel.RemoteChannel;
import com.tn.openrpc.channel.RemoteChannelException;
import com.tn.openrpc.channel.RemoteChannelRequest;
import com.tn.openrpc.channel.RemoteChannelResponse;
import com.tn.openrpc.serialization.SerializationException;
import com.tn.openrpc.serialization.Serializer;
import com.tn.openrpc.util.Factory;

/**
  * An implementation of <code>RemoteChannel</code> that uses <i>STOMP</i>.
 */
public class StompRemoteChannel implements RemoteChannel
{
  private static final long DEFAULT_TIMEOUT = 10 * 1000;

  private final StompClient stompClient;

  private Factory<String> idFactory;
  private String requestDestination;
  private String responseDestination;
  private Serializer<String> serializer;
  private long timeout;

  /**
   * Creates a new <code>StompRemoteChannel</code>.
   *
   * @param stompClient         the stomp client used to send and receive messages.
   * @param requestDestination  the request destination.
   * @param responseDestination the response destination.
   * @param idFactory           the factory used to create the various ids used by STOMP.
   * @param serializer          the serializer used to serialize and deserialize the message content.
   */
  public StompRemoteChannel(
    StompClient stompClient,
    String requestDestination,
    String responseDestination,
    Factory<String> idFactory,
    Serializer<String> serializer
  )
  {
    this.stompClient = stompClient;
    this.requestDestination = requestDestination;
    this.responseDestination = responseDestination;
    this.idFactory = idFactory;
    this.serializer = serializer;
    this.timeout = DEFAULT_TIMEOUT;
  }

  /**
   * Sets the timeout.
   */
  public void setTimeout(long timeout)
  {
    this.timeout = timeout;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public RemoteChannelResponse send(RemoteChannelRequest remoteChannelRequest) throws RemoteChannelException, TimeoutException
  {
    try
    {
      StompTemplate stompTemplate = new StompTemplate(stompClient, idFactory);
      stompTemplate.setTimeout(timeout);

      return serializer.deserialize(
        stompTemplate.send(
          requestDestination,
          responseDestination,
          createReceiptId(remoteChannelRequest),
          serializer.serialize(remoteChannelRequest)
        )
      );
    }
    catch (SerializationException e)
    {
      throw new RemoteChannelException("An error occurred de-serializing a response.", e);
    }
    catch (StompException e)
    {
      throw new RemoteChannelException("An error occurred sending a remote channel request.", e);
    }
  }

  /**
   * Creates a receipt-id from the <code>remoteChannelRequest</code>.
   */
  private String createReceiptId(RemoteChannelRequest remoteChannelRequest)
  {
    return remoteChannelRequest.getTarget() + "." + remoteChannelRequest.getMethodName();
  }
}
